package com.my.springboot.tuling.concurrent;

import com.google.common.collect.Lists;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;

/**https://blog.csdn.net/weixin_41544662/article/details/126463390
 * 1、线程的场景有两种：
 *    不需要关注线程方法的返回值。继承Thread类或者实现Runnable接口
 *    需要关注线程方法的返回值。CompleteFuture或者Fork/join框架
 * 2、数据丢失
 * 3、顺序问题：假如之前代码的执行顺序是：a,b,c，改成多线程执行之后，代码的执行顺序可能变成了：a,c,b。（这个跟cpu调度算法有关）
 *          3.1、Thread类的join方法它会让主线程等待子线程运行结束后，才能继续运行。
 *          3.2、可以使用JDK自带的Excutors类的newSingleThreadExecutor方法，创建一个单线程的线程池。
 *              使用Excutors类的newSingleThreadExecutor方法创建的单线程的线程池，使用了LinkedBlockingQueue作为队列，而此队列按 FIFO（先进先出）排序元素。
 *              添加到队列的顺序是a,b,c，则执行的顺序也是a,b,c。
 * 4、线程安全问题
 **/
public class ThreadDemo {
    private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,20,0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingDeque<>(10));

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadDemo threadDemo = new ThreadDemo();
        /**运行结果：
         * pool-1-thread-1线程获取用户信息：123
         * pool-1-thread-2线程获取积分信息：123
         * pool-1-thread-3线程获取成长值信息：123
         **/
        //threadDemo.getUser("123");
        /**
         * 运行结果： a c b
         **/
        //threadDemo.threadOrder();
        /**
         * 运行结果：a b c
         **/
        //threadDemo.threadJoin();
        /**
         * 运行结果：a b c
         **/
        //threadDemo.newSingleThreadExecutorDemo();

        threadDemo.countDownLatchDemo();



    }

    /**
     * @description:1、查询用户信息接口，需要返回用户基本信息、积分信息、成长值信息，而用户、积分和成长值，需要调用不同的接口获取数据。
     *              Java8以后通过CompleteFuture类，可以实现线程返回值
     * @author: lxk
     * @date: 2023/5/19 15:53
     **/
    private String getUser(String id) throws ExecutionException, InterruptedException {
        CompletableFuture userFuture  = CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName() + "线程获取用户信息：" + id);
            return true;
        },threadPoolExecutor);

        CompletableFuture bonusFuture = CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName() + "线程获取积分信息：" + id);
            return true;
        },threadPoolExecutor);

        CompletableFuture growthFuture = CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName() + "线程获取成长值信息：" + id);
            return true;
        },threadPoolExecutor);
        CompletableFuture.allOf(userFuture, bonusFuture, growthFuture).join();

        userFuture.get();
        bonusFuture.get();
        growthFuture.get();
        return id;
    }

    /**
     * @description:3、顺序问题：假如之前代码的执行顺序是：a,b,c，改成多线程执行之后，代码的执行顺序可能变成了：a,c,b。（这个跟cpu调度算法有关）
     * @author: lxk
     * @date: 2023/5/19 16:14
     **/
    private void threadOrder(){
        Thread thread1 = new Thread(() -> System.out.println("a"));
        Thread thread2 = new Thread(() -> System.out.println("b"));
        Thread thread3 = new Thread(() -> System.out.println("c"));

        thread1.start();
        thread2.start();
        thread3.start();
    }
    /**
     * @description:Thread类的join方法它会让主线程等待子线程运行结束后，才能继续运行。
     * @author: lxk
     * @date: 2023/5/19 16:18
     **/
    private void threadJoin() throws InterruptedException {
        Thread thread1 = new Thread(() -> System.out.println("a"));
        Thread thread2 = new Thread(() -> System.out.println("b"));
        Thread thread3 = new Thread(() -> System.out.println("c"));

        thread1.start();
        thread1.join();
        thread2.start();
        thread2.join();
        thread3.start();
    }

    /**
     * @description:使用Excutors类的newSingleThreadExecutor方法创建的单线程的线程池，使用了LinkedBlockingQueue作为队列，
     *              而此队列按 FIFO（先进先出）排序元素。添加到队列的顺序是a,b,c，则执行的顺序也是a,b,c。
     * @author: lxk
     * @date: 2023/5/19 16:23
     **/
    private void newSingleThreadExecutorDemo(){
        // 创建一个单线程的线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Thread thread1 = new Thread(() ->{System.out.println("a");});
        Thread thread2 = new Thread(() ->{System.out.println("b");});
        Thread thread3 = new Thread(() ->{System.out.println("c");});

        executorService.submit(thread1);
        executorService.submit(thread2);
        executorService.submit(thread3);

        executorService.shutdown();
    }

    /**
     * @description:CountDownLatch是一个同步工具类，它允许一个或多个线程一直等待，直到其他线程执行完后再执行。
     * @author: lxk
     * @date: 2023/5/19 16:26
     **/
    private void countDownLatchDemo(){
        CountDownLatch countDownLatch1 = new CountDownLatch(0);
        CountDownLatch countDownLatch2 = new CountDownLatch(1);
        CountDownLatch countDownLatch3 = new CountDownLatch(1);

        Thread thread1 = new Thread(new RunnableDemo(countDownLatch1,countDownLatch2,"a"));
        Thread thread2 = new Thread(new RunnableDemo(countDownLatch2,countDownLatch3,"b"));
        Thread thread3 = new Thread(new RunnableDemo(countDownLatch3,countDownLatch3,"c"));
        thread1.start();
        thread2.start();
        thread3.start();
        System.out.println("执行结束");
    }

//    private void initList(){
//        List<String> dataList = Arrays.asList("a","b","c");
//        List<String> list = Lists.newCopyOnWriteArrayList();
//        dataList.stream().map(
//                data -> CompletableFuture.supplyAsync(() -> query(list, data),threadPoolExecutor));
//        CompletableFuture.allOf();
//    }

    private void query(List<String> list, String id){
        list.add(id);
    }
}
